package utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

//如果需要事务管理,如commit,则调用getSqlSession().
//如果不需要事务管理,如查询,则调用getMapper().
public class MyBatisUtil {

    //sqlSessionFactory是单例模式，和缓存相关
    private static SqlSessionFactory sqlSessionFactory;
    //ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法.
    //它提供线程本地变量，访问这个ThreadLocal变量的每个线程都会有这个变量的一个副本，在实际多线程操作的时候，操作的是自己本地内存中的变量.
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<>();

    //在静态块中获取sqlSessionFactory
    static {
        try {
            //加载MyBatis配置文件
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //会话(连接)工厂,sqlSessionFactory是单例的
            sqlSessionFactory = sqlSessionFactoryBuilder.build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取sqlSessionFactory
    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    //获取SqlSession
    //参数为:是否事务自动提交
    //(1)如果只有一步操作,建议isAutoCommit=true.
    //(2)如果有2步以上的操作,建议isAutoCommit=false,
    //所有操作都成功了再sqlSession.commit(),会将前面所有步骤提交;如果有一个失败就立即会将其和前面的步骤回滚,try { 操作内容 } catch(Exception e) {sqlSession.rollback();};
    //否则成功一个步骤就会提交一个.
//    public static SqlSession getSqlSession(boolean isAutoCommit) {
//        SqlSession sqlSession = local.get();
//        if (sqlSession == null) {
//            //openSession()默认传入false即不会自动提交事务.需要写sqlSession.commit();
//            //openSession(true)则是事务自动提交,不需要写sqlSession.commit();.
//            sqlSession = sqlSessionFactory.openSession(isAutoCommit);
//            local.set(sqlSession);
//        }
//        return sqlSession;
//    }

    //手动事务管理:如果写成这样,则给外面的方法就是手动提交.
    public static SqlSession getSqlSession() {
        return getSqlSession(false);
    }

    private static SqlSession getSqlSession(boolean isAutoCommit) {
//        如果线程里面有SqlSession就直接用，没有就创建一个并放入线程。
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
            //openSession()默认传入false即不会自动提交事务.需要写sqlSession.commit();
            //openSession(true)则是事务自动提交,不需要写sqlSession.commit();.
            sqlSession = sqlSessionFactory.openSession(isAutoCommit);
            local.set(sqlSession);
        }
        return sqlSession;
    }

    //T是泛型，可以代表任何对象,传进来Student就返回Student
    //查询操作,不需要sqlSession.commit()的操作的,可以调用这个获取Dao
    //自动事务管理:因为直接拿到了DAO,而没有SqlSession,所以默认自动事务提交isAutoCommit=true.
    public static <T extends Object> T getMapper(Class<T> c) {
        SqlSession sqlSession = getSqlSession(true);
        //获取dao的Mapper对象,如果c传入StudentDao.class,则返回StudentDao
        //相当于 T daoMapper = sqlSession.getMapper(c);
        return sqlSession.getMapper(c);
    }


}
